package com.yjzx.util.common.util.collection;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author guozhi
 * @date 2024/9/23
 * @description Collection1Util
 */
public class Collection1Util {
    public static <T> Predicate<T> toTrue() {
        return t -> true;
    }

    /**
     * 因stream流 value无法存在null，重写tomap
     *
     * @param collections
     * @param keyMapper
     * @param valueMapper
     * @param <T>
     * @param <K>
     * @param <V>
     * @return
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collections, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        return toMap(collections, toTrue(), keyMapper, valueMapper);
    }

    public static <T, K, V> Map<K, V> toMap(Collection<T> collections, Predicate<? super T> predicate, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        int size = collections.size();
        size = hashMapSize(size);
        Map<K, V> map = new HashMap<>(size);
        for (T collection : collections) {
            if (predicate.test(collection)) {
                map.put(keyMapper.apply(collection), valueMapper.apply(collection));
            }
        }
        return map;
    }

    public static <T, K, V> Map<K, List<V>> toMapValueList(Collection<T> collections, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        return toMapValueList(collections, toTrue(), keyMapper, valueMapper);
    }
    public static <T, K, V> Map<K, List<V>> toMapValueList(Collection<T> collections,Predicate<? super T> predicate, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        Map<K, List<V>> map = new HashMap<>(16);
        for (T collection : collections) {
            if (predicate.test(collection)) {
                K k = keyMapper.apply(collection);
                V v = valueMapper.apply(collection);
                map.putIfAbsent(k, new ArrayList<>());
                map.get(k).add(v);
            }
        }
        return map;
    }

    public static <T, K, V> Map<K, Set<V>> toMapValueSet(Collection<T> collections, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        return toMapValueSet(collections, toTrue(), keyMapper, valueMapper);
    }

    public static <T, K, V> Map<K, Set<V>> toMapValueSet(Collection<T> collections, Predicate<? super T> predicate, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        Map<K, Set<V>> map = new HashMap<>(16);
        for (T collection : collections) {
            if (predicate.test(collection)) {
                K k = keyMapper.apply(collection);
                V v = valueMapper.apply(collection);
                map.putIfAbsent(k, new HashSet<>());
                map.get(k).add(v);
            }
        }
        return map;
    }

    public static <R, T> List<R> toList(Collection<T> collections, Function<? super T, ? extends R> valueMapper) {
        return toList(collections, toTrue(), valueMapper);
    }

    public static <R, T> List<R> toList(Collection<T> collections, Predicate<? super T> predicate, Function<? super T, ? extends R> valueMapper) {
        int size = collections.size();
        List<R> list = new ArrayList<>(size);
        for (T collection : collections) {
            if (predicate.test(collection)) {
                list.add(valueMapper.apply(collection));
            }
        }
        return list;
    }

    public static <R, T> Set<R> toSet(Collection<T> collections, Function<? super T, ? extends R> valueMapper) {
        return toSet(collections, toTrue(), valueMapper);
    }

    public static <R, T> Set<R> toSet(Collection<T> collections, Predicate<? super T> predicate, Function<? super T, ? extends R> valueMapper) {
        int size = collections.size();
        size = hashMapSize(size);
        Set<R> set = new HashSet<R>(size);
        for (T collection : collections) {
            if (predicate.test(collection)) {
                set.add(valueMapper.apply(collection));
            }
        }
        return set;
    }

    private static int hashMapSize(int size) {
        size = (int) (size / 0.75);
        int a = 1;
        while (size > 1) {
            size = size >> 1;
            a++;
        }
        size = 1 << Math.min(a, 8);
        return size;
    }

    /**
     * 获取list 左差集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> List<T> leftDifferenceSetList(List<T> list1, List<T> list2) {
        Set<T> tempSet = nullListToEmptySet(list2);
        return nullListToEmptyList(list1).stream().filter(str -> !tempSet.contains(str)).collect(Collectors.toList());
    }

    /**
     * 获取list 右差集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> List<T> rightDifferenceSetList(List<T> list1, List<T> list2) {
        Set<T> tempSet = nullListToEmptySet(list1);
        return nullListToEmptyList(list2).stream().filter(str -> !tempSet.contains(str)).collect(Collectors.toList());
    }

    /**
     * 获取list 差集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> List<T> differenceSetList(List<T> list1, List<T> list2) {
        Set<T> tempSet1 = nullListToEmptySet(list1);
        Set<T> tempSet2 = nullListToEmptySet(list2);
        return Stream.concat(tempSet1.stream().filter(str -> !tempSet2.contains(str)), tempSet2.stream().filter(str -> !tempSet1.contains(str))).collect(Collectors.toList());
    }

    /**
     * 获取list 并集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> List<T> joinList(List<T> list1, List<T> list2) {
        Set<T> tempSet = nullListToEmptySet(list1);
        tempSet.addAll(nullListToEmptyList(list2));
        return new ArrayList<>(tempSet);
    }

    /**
     * 获取list 交集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> List<T> intersectionList(List<T> list1, List<T> list2) {
        Set<T> tempSet =nullListToEmptySet(list1);
        return nullListToEmptyList(list2).stream().filter(tempSet::contains).collect(Collectors.toList());
    }

    public static <T> List<T> nullListToEmptyList(List<T> list) {
        return EmptyUtils.isEmpty(list) ? Collections.emptyList() : list;
    }

    public static <T> Set<T> nullListToEmptySet(List<T> list) {
        return EmptyUtils.isEmpty(list) ? Collections.emptySet() : new HashSet<>(list);
    }

}
